package com.onpositive.wikistorage;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.URL;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Display;

import storage.v1.StorageWithRegistry;

public class PictureManager {

	private static final String PIC_DATA_SUBPATH = "picdata/";
	// public static final String PICTURE_LINKS_ARRAY_PROPERY_NAME =
	// "Picture links" ;
	public static final String WIKIPEDIA_FILE_PATH = "http://en.wikipedia.org/wiki/File:";
	public static final String picturesSubpath = "pictures/";
	public static final String validPicturesList = "valid-pics.txt";

	static private HashMap<String, PictureManager> map = new HashMap<String, PictureManager>();

	HashMap<String, String> validPics = null;

	public static PictureManager getInstance(String rootPath) {

		PictureManager instance = map.get(rootPath);
		if (instance == null) {
			instance = new PictureManager(rootPath);
			map.put(rootPath, instance);
		}
		return instance;
	}

	private PictureManager(String rootPath) {
		this.rootPath = constructPicturePath(rootPath);
		new File(this.rootPath).mkdirs();
		initValidPicsSet();
	}

	public static String constructPicturePath(String rootPath) {
		return rootPath
				+ (rootPath.endsWith("/") || rootPath.endsWith("\\") ? "" : "/")
				+ picturesSubpath;
	}

	String rootPath;

	public boolean checkPicturePresence(String wikiPath, String docId) {
		String str0 = convertToLocalPath(wikiPath, docId);
		String s = validPics.get(str0);
		if (s != null) {
			Boolean val = Boolean.parseBoolean(s);
			if (val != null)
				return val;
		}
		boolean val = checkPictureLocalPresence(str0);
		validPics.put(str0, Boolean.toString(val));
		return val;
	}

	private void initValidPicsSet() {
		validPics = new HashMap<String, String>();
		String pth = this.rootPath + validPicturesList;
		try {
			BufferedReader bReader = new BufferedReader(new InputStreamReader(
					new FileInputStream(pth)));

			String line = bReader.readLine(), val;
			while (line != null) {
				val = bReader.readLine();
				validPics.put(line, val);
				line = bReader.readLine();
			}
		} catch (FileNotFoundException e) {
			// e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public void saveValidPicsList() {
		String pth = this.rootPath + validPicturesList;
		try {

			StringBuilder builder = new StringBuilder();
			for (Map.Entry<String, String> entry : validPics.entrySet()) {
				builder.append(entry.getKey());
				builder.append("\n");
				builder.append(entry.getValue());
				builder.append("\n");
			}
			OutputStreamWriter wrt = new OutputStreamWriter(
					new FileOutputStream(pth));
			wrt.write(builder.substring(0, builder.length() - 1));

		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public ArrayList<byte[]> getPictures(Collection<String> wikiPaths,
			String docId) {
		ArrayList<byte[]> result = new ArrayList<byte[]>();

		for (String s : wikiPaths) {
			byte[] pic = getPicture(s, docId);
			if (pic != null)
				result.add(pic);
		}

		return result;
	}

	public byte[] getPicture(String wikiPath, String docId) {
		String localPath = convertToLocalPath(wikiPath, docId);
		File f = new File(localPath);
		if (f.exists()) {
			try {
				int l = (int) f.length();
				byte[] result = new byte[l];
				FileInputStream fis = new FileInputStream(f);
				fis.read(result, 0, l);

				return result;

			} catch (FileNotFoundException e) {
				e.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			}
		} else
			return grabPictureFromNet(wikiPath, docId);

		return null;
	}

	public void grabPicturesFromNet(Collection<String> wikiPaths,
			final String docId) {
		ArrayList<Thread> sm = new ArrayList<Thread>();
		for (final String s : wikiPaths) {
			Thread sa = new Thread() {
				@Override
				public void run() {
					grabPictureFromNet(s, docId);
					System.out.print(docId + ": " + s + "\n");
					super.run();
				}
			};
			sa.start();
			sm.add(sa);
		}
		for (int a = 0; a < 20; a++) {
			boolean hasAlive=false;
			for (Thread c : sm) {
				try {
					if (c.isAlive()){
						hasAlive=true;
					}
					c.join(1000);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			if (!hasAlive){
				break;
			}
		}
	}

	public byte[] grabPictureFromNet(String wikiPath, String docId) {
//		if (wikiPath.startsWith("http:"))
//			return null;
		String localPath = convertToLocalPath(wikiPath, docId);
		new File(this.rootPath + docId + "/").mkdirs();
		File f = new File(localPath);

		if (f.exists()&&f.length()>0)
			return null;

		try {

			String link = constructURL(wikiPath);
			if (link == null)
				return null;

			byte[] pic = loadPicture(link);
			if( pic==null){
				System.out.println("A");
			}
			f.getParentFile().mkdirs();
			FileOutputStream fos = new FileOutputStream(f);
			try {
				fos.write(pic);
				fos.close();
			} catch (NullPointerException e) {
				e.printStackTrace();
			}
			return pic;

		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
	}

	public String convertToLocalPath(String s, String docId) {
		s=s.replace(":", "_");
		s=s.replace("\"", "_");
		File rootFolder = new File(rootPath);
		File docFolder = new File(rootFolder,docId);
		File picFile = new File(docFolder,s);
		return picFile.getAbsolutePath();
	}

	private boolean checkPictureLocalPresence(String path) {

		if (path == null || path.length() == 0)
			return false;

		Image image = null;
		try {
			image = new Image(Display.getDefault(), path);
		} catch (Exception e) {
			return false;
		}
		if (image == null || image.isDisposed()) {
			return false;
		} else {
			image.dispose();
			return true;
		}
	}

	private byte[] loadPicture(String link) {
		if (link == null)
			return null;

		try {

			URL url = new URL(link);

			BufferedInputStream picStream = new BufferedInputStream(
					url.openStream());

			ByteArrayOutputStream os = new ByteArrayOutputStream();

			for (int n = picStream.read(); n != -1; n = picStream.read())
				os.write((byte) n);

			os.close();

			return os.toByteArray();
		} catch (IOException e) {
			System.out.print("cannot find picture: ");
			System.out.print(link);
			System.out.print("\n");
		}
		return null;
	}

	StorageWithRegistry docDirStorage;
	StorageWithRegistry picFileStorage;

	private void initDirAndFileStorages() {

		if (docDirStorage == null)
			docDirStorage = new StorageWithRegistry(rootPath, "doc-dir");

		docDirStorage.initMap();

		if (picFileStorage == null)
			picFileStorage = new StorageWithRegistry(rootPath, "doc-dir");

		picFileStorage.initMap();
	}

	public String constructURL(String s) throws IOException {
		// if( s.startsWith("http://") )
		// return s ;
		s = s.replace("commns", "commons");
		if (s.startsWith("http:"))
			return s;

		if (!isPic(s))
			return null;
		
		String str = s.replace(" ", "_");
		
		String str0 = URLEncoder.encode(str, "UTF-8");

		// str = str.replace("&", "%26") ;
		str = str0;// performUrlEncoding( str0 ) ;

		URL url = new URL(WIKIPEDIA_FILE_PATH + str);

		String strLC = str.toLowerCase();
		System.out.println(str);
		String picDataStringContent = getPicDataContent(url);
		if (picDataStringContent == null)
			return null;
		String contentLC = picDataStringContent.toLowerCase();
		int endIndex = 0;
		int beginIndex = -1;

		try {
			for (; beginIndex == -1;) {
				endIndex = contentLC.indexOf(strLC, endIndex);
				if(endIndex<0)
					break;
				String tmp = contentLC.substring(endIndex);
				String tmp2 = contentLC.substring(0, endIndex);
				beginIndex = contentLC.lastIndexOf(
						"//upload.wikimedia.org/wikipedia/", endIndex);
				if (endIndex - beginIndex
						- "//upload.wikimedia.org/wikipedia/".length() > 40)
					beginIndex = -1;
				endIndex += strLC.length();

				// beginIndex =
				// content.lastIndexOf("http://upload.wikimedia.org/wikipedia/commons/",
				// endIndex) ;

			}
			if(endIndex>=0){

			String link = "http:"
					+ picDataStringContent.substring(beginIndex, endIndex);
			int indexOf = link.indexOf("/thumb/");
			if (indexOf!=-1){
				link=link.substring(0,indexOf)+link.substring(indexOf+6);
			}
			link=link.replace("commns","commons");
			return link;
			}

		} catch (StringIndexOutOfBoundsException e) {
			// System.out.print(fileName + ", " + str +'\n');
			e.printStackTrace();
		}
		return null;
	}

	private StorageWithRegistry picDataPageStorage;

	private void initPicDataStorage() {
		if (picDataPageStorage == null)
			picDataPageStorage = new StorageWithRegistry(rootPath
					+ PIC_DATA_SUBPATH, "pic-data");

		picDataPageStorage.initMap();
	}

	private String getPicDataContent(URL url) throws IOException {

		initPicDataStorage();
		String content = picDataPageStorage.getContent(url.toExternalForm());
		if (content != null)
			return content;

		BufferedInputStream iStream;
		try {
			iStream = new BufferedInputStream(url.openStream());
		} catch (FileNotFoundException e) {
			// e.printStackTrace() ;
			return null;
		}
		byte[] byteArr = new byte[iStream.available() + 1];

		int i = -1;
		int l = byteArr.length;
		for (int n = iStream.read(); n != -1; n = iStream.read()) {
			if (i < l - 1)
				byteArr[++i] = (byte) n;
			else {
				int l0 = iStream.available();
				l0 = l0 > 0 ? l + l0 : (int) (l * 1.2) + 1;

				byte[] newByteArr = new byte[l0];
				System.arraycopy(byteArr, 0, newByteArr, 0, l);
				byteArr = newByteArr;
				l = byteArr.length;
			}
		}
		initPicDataStorage();

		content = new String(byteArr);
		picDataPageStorage.addContent(url.toString(), content);

		return content;
	}

	public static ArrayList<String> urlEncodingStrings = new ArrayList<String>();
	static {
		urlEncodingStrings.add("%");
		urlEncodingStrings.add("%25");

		urlEncodingStrings.add("!");
		urlEncodingStrings.add("%21");
		urlEncodingStrings.add("*");
		urlEncodingStrings.add("%2A");
		urlEncodingStrings.add("'");
		urlEncodingStrings.add("%27");
		urlEncodingStrings.add("(");
		urlEncodingStrings.add("%28");
		urlEncodingStrings.add(")");
		urlEncodingStrings.add("%29");

		urlEncodingStrings.add("$");
		urlEncodingStrings.add("%24");
		urlEncodingStrings.add("&");
		urlEncodingStrings.add("%26");
		urlEncodingStrings.add("+");
		urlEncodingStrings.add("%2B");
		urlEncodingStrings.add(",");
		urlEncodingStrings.add("%2C");
		urlEncodingStrings.add("/");
		urlEncodingStrings.add("%2F");
		urlEncodingStrings.add(":");
		urlEncodingStrings.add("%3A");
		urlEncodingStrings.add(";");
		urlEncodingStrings.add("%3B");
		urlEncodingStrings.add("=");
		urlEncodingStrings.add("%3D");
		urlEncodingStrings.add("?");
		urlEncodingStrings.add("%3F");
		urlEncodingStrings.add("@");
		urlEncodingStrings.add("%40");

		urlEncodingStrings.add("\"");
		urlEncodingStrings.add("%22");
		urlEncodingStrings.add("<");
		urlEncodingStrings.add("%3C");
		urlEncodingStrings.add(">");
		urlEncodingStrings.add("%3E");

		urlEncodingStrings.add("{");
		urlEncodingStrings.add("%7B");
		urlEncodingStrings.add("}");
		urlEncodingStrings.add("%7D");
		urlEncodingStrings.add("|");
		urlEncodingStrings.add("%7C");
		urlEncodingStrings.add("\\");
		urlEncodingStrings.add("%5C");
		urlEncodingStrings.add("^");
		urlEncodingStrings.add("%5E");
		urlEncodingStrings.add("~");
		urlEncodingStrings.add("%7E");
		urlEncodingStrings.add("[");
		urlEncodingStrings.add("%5B");
		urlEncodingStrings.add("]");
		urlEncodingStrings.add("%5D");
		urlEncodingStrings.add("`");
		urlEncodingStrings.add("%60");
	}

	public static String performUrlEncoding(String str) {
		int s = urlEncodingStrings.size();
		for (int i = 0; i < s; i += 2)
			str = str.replace(urlEncodingStrings.get(i),
					urlEncodingStrings.get(i + 1));

		return str;
	}

	static boolean isPic(String str) {
		final String[] extArr = { "jpg", "jpeg", "JPG", "JPEG", "Jpeg", "Jpg",
				"png", "PNG", "gif", "GIF", "tif", "TIF", "tiff", "TIFF" };
		boolean result = false;

		for (String s : extArr)
			result = result || str.endsWith(s);

		return result;
	}

	public void dispose() {
		this.picDataPageStorage = null;
	}

}
